/******************************************************************************
 *      Mestrado em Ciência da Computação - UFMS
 *		Redes de Computadores - Prof. Dr. Ronaldo Alves Ferreira
 *		Alunos: Geraldo Landre, Marcel Tolentino, Vinícius Bueno
 *****************************************************************************/

#include "http_semaphore.h"
#include "dirmanip.h" // para usar a função read_string

/*
	IMPLEMENTAÇÃO DAS FUNÇÕES DE http_semaphore.h
*/

/*	Instancia um pool de threads com n posições
	e o retorna	*/
pool_th pool_init(int n)
{
	pool_th pool;
	
	// inicializa o semaforo
	//pthread_mutex_init(&semaphore, NULL);
	
	// atribui o no. de threads
	pool.nthreads = n;
	// reserva espaço para os ponteiros de threads
	pool.threads = (thread_t*) safe_alloc( n * sizeof(thread_t) );
	
	return pool;
}

/*	Cancela a execução de todas as threads
	e libera o espaço alocado para as mesmas	*/
void pool_destroy(pool_th p)
{
	if(p.threads != NULL)
	{
		int i;
		for(i = 0; i < p.nthreads; ++i)
		{
			// finaliza a thread
			pthread_cancel(p.threads[i].thread);
			// TO DO: verificar retorno da função pthread_cancel
		}
		
		// libera espaço na memória
		free((void*)p.threads);
		p.threads = NULL;
	}
}

/*
	Inicia as threads chamando a função passada por parâmetro
	passando os parâmetros também passados.
	Parâmetro:
	- pool_t p:			pool de threads a iniciar
	- int sockfd:		socket do servidor (listening socket)
*/
void pool_start(pool_th pool, int sockfd)
{
	if(pool.threads == NULL)
	{
		// casso a função seja chamada
		// antes da função pool_init
		return;
	}
	
	// inicia o semáforo
	pthread_mutex_init( &semaphore, NULL);
	
	int i, iret;
	
	for(i = 0; i < pool.nthreads; ++i)
	{
		
		pool.threads[i].id = i; // identifica a thread
		
		exec_params* params = safe_alloc(sizeof(exec_params));
		
		params->id = pool.threads[i].id;
		params->sockfd = sockfd;						// socket fd do servidor
		params->new_fd = 0;								// socket fd para usar no accept
		params->client_len = sizeof(params->client);	// tamanho da estrutura
		
		// TO DO:	- Verificar os parâmetros que devem ser passados para a função
		//			pthread_create
		//			- Passar a estrutura de parâmetros para a função
		iret = pthread_create(& (pool.threads[i].thread), NULL, execute, (void*)params);
		
		// TO DO:	tratar erro de criação de thread
		// 			verificar se vai vai finalizar o programa
		printf("create:\t%d\n", i);
		if(iret < 0)
			printf("error creating thread\n");
			
	}// for
}

/*	função a ser executada pelas threads
	chama o accept	*/
void execute_accept(exec_params params)
{
    // efetua a trava do semáforo
    pthread_mutex_lock( &semaphore );

    // efetuar o accept
    params.new_fd = accept( params.sockfd, (struct sockaddr *)& (params.client) ,&params.client_len);

    // destrava o semáforo
    pthread_mutex_unlock( &semaphore );

    if(params.new_fd == -1)
    {
        perror("erro no accept");
    }
    else
    {
        fprintf(stdout, "thread %d accepted a new request.\n", params.id);
    }

    // trata a conexão do cliente
    connection_handle(params.new_fd);
}

/*	função a ser executada pelas threads.
	Esta função é sincronizada pelo semáforo
	Parâmetros
		- void* p: parâmetros (struct: exec_params)	*/
void* execute(void* p)
{
	exec_params* params = (exec_params*) p;
	
	int id = params->id;
	
	while(1)
	{
		if(lg_listen_started)
		{
                 
			// TO DO: chamar o accept
			//sem_testing(id);
			execute_accept(*params);
		} 
		else
		{
			printf("[%d]: waiting for function listen to be called... \n", id);
		}
		
		wait_( get_random_number(3) );
	} // endwhile

	return NULL;
}

/*	Função utilizada para testes
	simula a execução de um servidor multithread	*/
void sem_simulate(char* port, int nthreads)
{
	set_rand_seed(time(NULL));
	
	pool_th pool = pool_init(nthreads);
	pool_start(pool, 0); // sem linkar ao servidor
	
	char* input = NULL;
	do
	{
		wait_(10);
		read_string(&input);
	}
	while(strcmp(input, "quit"));
	
	pool_destroy(pool);
}

void sem_testing(int id)
{
    pthread_mutex_lock(&semaphore);
    printf("---\nstart:\t%d\n", id);
    int number = get_random_number(10);
    printf("wait: %d sec.\n", number);
    wait_( number ); // espera de 1 a 10 segundos
    printf("finish:\t%d\n---\n", id);
    pthread_mutex_unlock(&semaphore);
}

